/**
 * WangSong 2009-03-13 11:17 增加了其他异常在ErrorMessage 
 * WangSong 2009-6-4上午10:10:13  通过系统异常创建时，把toString信息放到了Message中
 * WangSong 2009-6-6 上午09:48:26 2.0 使用父类堆栈保存轨迹,并获取出错的首个项目类作为地址
 */
package cn.biplam.common.wrapper;

import java.util.ArrayList;

import cn.biplam.common.tools.DEBUG;

/**
 * 自定义异常信息，继承RemoteException用于远程调用异常返回
 * @author 	WangSong Mar 10, 2009 10:43:51 AM
 * @version 2.0
 */
public class ErrorMessage extends Exception {

	private static final long serialVersionUID = 2448911075168442644L;
	private String sAddress;	//异常出错地址
	private String sDate;		//异常发生时间
	private String sErrorClass;	//异常类型
	private String sMessage=null;	//异常信息
	private String sPageID;		//调用页面
	private String sUserID;		//调用用户
	private StackTraceElement[] stackTrace;	//异常轨迹(仅保留biplam包下的类)

	/**
	 * 功能：构造方法
	 */
	@SuppressWarnings("unused")
	private ErrorMessage(){}

	/**
	 * 构造方法	传入异常信息初始化
	 * @param message  异常信息
	 */
	public ErrorMessage(String message) {
		this(null,message);
	}


	/**
	 * 构造方法	传入一个异常类初始化
	 * @param mException  Exception对象
	 */
	public ErrorMessage(Throwable cause){
		this(cause,null);
	}

	/**
	 * 不需要附加消息的初始化<BR>
	 * 用于反射抛出内嵌异常时,其消息已经在Throwable类中
	 * @param cause
	 * @param noMessage
	 */
	public ErrorMessage(Throwable cause,boolean noMessage){
		this(cause,null,true);
	}

	/**
	 * 构造方法	传入一个Throwable和异常信息初始化
	 * @param cause
	 * @param message
	 */
	public ErrorMessage(Throwable cause,String message){
		this(cause, message, false);
	}

	/**
	 * 可指定是否附加消息,如果noMessage=false,则忽略传入的message
	 * @param cause
	 * @param message
	 * @param noMessage
	 */
	private ErrorMessage(Throwable cause,String message,boolean noMessage){
		this.setMessage(message);
		if(cause!=null){
			if(cause.getMessage()!=null && !cause.getMessage().equals("null")){
				if(noMessage){
					this.setMessage(cause.getMessage());
				}else{
					this.setMessage(message+","+cause.getMessage());
				}
			}
			super.setStackTrace(cause.getStackTrace());

			this.sErrorClass=cause.getClass().getName();
		}
		//寻找第一个cn.biplam包内的出错类(必须在super.setStackTrace后执行)
		this._findAddress();
		if(this.getClass().getName().equals(this.sErrorClass)){
			//如果是自定义异常,则异常类为空
			this.sErrorClass=null;
		}
	}

	/**
	 * 构造方法
	 * @param userID	调用用户
	 * @param pageID	调用页面
	 * @param date		异常发生时间
	 * @param message	异常信息
	 */
	public ErrorMessage(String userID, String pageID, String date,
			String message) {
		this(message);
		sUserID = userID;
		sPageID = pageID;
		sDate = date;
	}

	/**
	 * 构造方法
	 * @param userID	调用用户
	 * @param pageID	调用页面
	 * @param date		异常发生时间
	 * @param message	异常信息
	 * @param Exception	
	 */
	public ErrorMessage(String userID, String pageID, String date,
			String message, Exception mException) {
		this(mException,message);
		sUserID = userID;
		sPageID = pageID;
		sDate = date;
	}

	/**
	 * 查找在biplam包内的出错类地址
	 */
	private void _findAddress(){
		StackTraceElement[] stack=super.getStackTrace();
		for(int i=0;i<stack.length;i++){
			if(isBiplamStackTraceElement(stack[i])){
				this.sAddress=stack[i].toString();
				break;
			}else if(stack[i].toString().startsWith("java") && this.sAddress==null){
				this.sAddress=stack[i].toString();
			}
		}
	}

	/**
	 * 设置异常堆栈,删除非biplam包下的类
	 * @param cause	异常类
	 * @return		StackTraceElement[]
	 */
	private StackTraceElement[] getStackTrace(StackTraceElement[] trace){
		ArrayList<StackTraceElement> tmp=new ArrayList<StackTraceElement>();
		for(StackTraceElement ite:trace){
			if(isBiplamStackTraceElement(ite)){
				tmp.add(ite);
			}
		}

		StackTraceElement[] ret=new StackTraceElement[tmp.size()];
		for(int i=0;i<ret.length;i++){
			ret[i]=tmp.get(i);
		}
		return ret;
	}

	/**
	 * 判断一个异常轨迹类是否是biplam包下的类
	 * @param element	StackTraceElement
	 * @return		boolean
	 */
	private boolean isBiplamStackTraceElement(StackTraceElement element){
		String[] as=element.toString().split("\\.");
		if(as.length>0){
			if("cn".equals(as[0]) && "biplam".equals(as[1])){
				return true;
			}
		}
		return false;
	}

	/**
	 * @return the sDate
	 */
	public String getDate() {
		return sDate;
	}

	/**
	 * 返回异常信息
	 * @return the sMessage
	 */
	public String getMessage() {
		if(this.sMessage==null){
			return this.sErrorClass+":"+"Unhandled exception found!";
		}else{
			return this.sMessage;
		}
	}

	/**
	 * @return the sPageID
	 */
	public String getPageID() {
		return sPageID;
	}

	/**
	 * @return the sUserID
	 */
	public String getUserID() {
		return sUserID;
	}

	/**
	 * 打印堆栈信息到屏幕
	 */
	@Override
	public void printStackTrace() {
		DEBUG.println(this.getStackTraceInfo());
//		if(this.sErrorClass!=null && this.sMessage==null){//如果原因是系统异常类,且未设置异常消息(即:未捕获)
//			DEBUG.println(this.getFullStackTraceInfo());
//		}else{ //如果是自定义异常或者已捕获的系统异常
//			DEBUG.println(this.getStackTraceInfo());
//		}
	}

	/**
	 * 返回堆栈信息文本
	 * @return
	 */
	public String getStackTraceInfo(){
		this.stackTrace=this.getStackTrace(super.getStackTrace());
		StringBuilder sb = new StringBuilder();
		this.getDescription(sb);
		for(int i=0;i<this.stackTrace.length;i++){
			sb.append("\n\tat " + stackTrace[i]);
		}
		return sb.toString();
	}
	
	/**
	 * 返回完整的堆栈信息文本
	 * @return
	 */
	public String getFullStackTraceInfo(){
		StringBuilder sb = new StringBuilder();
		this.getDescription(sb);
		StackTraceElement[] stack=super.getStackTrace();
		for(int i=0;i<stack.length;i++){
			sb.append("\n\tat " + stack[i]);
		}
		return sb.toString();
	}

	/**
	 * @param date the sDate to set
	 */
	public void setDate(String date) {
		sDate = date;
	}

	/**
	 * @param message the sMessage to set
	 */
	public void setMessage(String message) {
		if(message!=null && message!="null"){
			this.sMessage=message;
		}
	}

	/**
	 * @param pageID the sPageID to set
	 */
	public void setPageID(String pageID) {
		sPageID = pageID;
	}

	/**
	 * @param userID the sUserID to set
	 */
	public void setUserID(String userID) {
		sUserID = userID;
	}

	/**
	 * 获得关于出错信息的完整描述
	 * @param sb
	 */
	public void getDescription(StringBuilder sb){
		if(this.sErrorClass!=null){
			sb.append(this.sErrorClass);
			if(this.sMessage!=null) sb.append(":");
		}
		if(this.sMessage!=null){
			sb.append(this.sMessage);
		}
	}

	/**
	 * 输出异常信息
	 * @return
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		this.getDescription(sb);
		if(this.sAddress!=null){
			sb.append("\n\tat ");
			sb.append(this.sAddress);
		}
		return sb.toString();
	}

	public String getAddress() {
		return sAddress;
	}
}
